﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using CodeGame.ServerObjects;
using CodeGame.Networking;
using CodeGame.Networking.Serverside;
using CodeGame.Networking.Serverside.ClientServerPackets;

using System.Threading;
using System.Reflection;

using Lidgren.Network;

namespace CodeGame
{
    internal class Timer
    {
        public int Current;
        public int Max;
        public DedicatedServer.TimerRef Callback;

        public Timer(int max, DedicatedServer.TimerRef callback)
        {
            this.Current = 0;
            this.Max = max;
            this.Callback = callback;
        }

    }

    static class DedicatedServer
    {
        public delegate void TimerRef();

        static NetServer Server;

        static Dictionary<byte, ServerPacket> Packets = new Dictionary<byte, ServerPacket>();
        static List<Timer> Timers = new List<Timer>();

        public static void Start()
        {
            ServerSingletons.Players = new PlayerManager();
            ServerSingletons.Database = new DatabaseManager();
            ServerSingletons.Message = new MessagingManager();
            ServerSingletons.Hooks = new HookManager();
            ServerSingletons.Scripting = new ServerObjects.ScriptingManager();

            LoadPackets(Assembly.GetEntryAssembly(), "CodeGame.Networking.Serverside.ClientServerPackets");

            NetworkingGlobals.InitConfigs();
            Server = new NetServer(NetworkingGlobals.ServerConfig);
            Server.Start();

            Thread timers = new Thread(new ThreadStart(TimerLoop));
            timers.IsBackground = true;
            timers.Start();

            ServerSingletons.World = new WorldManager();

            while (true)
            {
                NetIncomingMessage message = Server.ReadMessage();

                if (message == null)
                {
                    Thread.Sleep(10);
                    continue;
                }

                switch (message.MessageType)
                {
                    case NetIncomingMessageType.ConnectionApproval:
                        Console.WriteLine(message.SenderEndpoint.ToString() + " : Requesting approval");
                        string nick = message.ReadString();

                        Player ply = new Player(nick, message.SenderConnection);

                        message.SenderConnection.Approve();

                        ply.GiveResouces(1000);
                        ServerSingletons.Players.Add(ply);
                        ServerProtocolAuth.SendTokenChange(ply);

                        break;
                    case NetIncomingMessageType.Data:
                        HandleData(message);
                        break;
                    case NetIncomingMessageType.DebugMessage:
                    case NetIncomingMessageType.ErrorMessage:
                    case NetIncomingMessageType.VerboseDebugMessage:
                    case NetIncomingMessageType.WarningMessage:
                        Console.WriteLine(message.MessageType.ToString() + " : " + message.ReadString());
                        break;
                    case NetIncomingMessageType.StatusChanged:
                        Console.WriteLine(message.SenderEndpoint.ToString() + " : " + message.SenderConnection.Status.ToString());
                        break;
                    default:
                        break;
                }

                Thread.Sleep(0);
            }
        }

        public static void AddTimer(int maxAmount, TimerRef callback)
        {
            Timers.Add(new Timer(maxAmount, callback));
        }

        private static void TimerLoop()
        {
            Thread.Sleep(500);
            while (true)
            {
                foreach (Timer item in Timers)
                {
                    if (item.Current > item.Max)
                    {
                        item.Current = 0;
                        item.Callback();
                    }
                    item.Current += 1;
                }

                Thread.Sleep(100);
            }


        }

        public static void HandleData(NetIncomingMessage message)
        {
            byte type = message.ReadByte();

            if (ServerSingletons.Players.CheckPlayer(message))
            {
                if (Packets.ContainsKey(type))
                {
                    ServerPacket pack = (ServerPacket)(Packets[type].Clone());
                    pack.sender = null;
                    pack.Handle(message, message.SenderConnection);
                }
            }
            else
            {
                Console.WriteLine("Auth for " + ServerSingletons.Players.GetPlayer(message.SenderConnection).Nickname + " failed");
                message.SenderConnection.Disconnect("Auth Failed");
            }

        }

        public static void LoadPackets(Assembly asm, string space)
        {
            foreach (Type item in asm.GetTypes())
            {
                if (item.Namespace == space)
                {
                    object obj = asm.CreateInstance(item.FullName);
                    ServerPacket pack = (ServerPacket)obj;
                    Packets.Add((byte)pack.GetID(), pack);
                }
            }
        }

        public static void SendMessage(NetOutgoingMessage msg)
        {
            Server.SendToAll(msg, NetDeliveryMethod.ReliableOrdered);
        }

        public static void SendMessage(NetOutgoingMessage msg, NetConnection conn)
        {
            Server.SendMessage(msg, conn, NetDeliveryMethod.ReliableOrdered);
        }

        public static NetOutgoingMessage CreateMessage(ServerClientNet type)
        {
            NetOutgoingMessage msg = Server.CreateMessage();
            msg.Write((byte)type);
            return msg;
        }

        public static void CreateListenServer()
        {
            Thread thread = new Thread(new ThreadStart(DedicatedServer.Start));
            thread.IsBackground = true;
            thread.Start();
            Thread.Sleep(500);
        }
    }
}
